package no.uib.hplims.views.peptides;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import org.apache.poi.util.IOUtils;
import org.vaadin.appfoundation.persistence.facade.FacadeFactory;

import no.uib.hplims.MyVaadinApplication;
import no.uib.hplims.models.peptideinformation.PeptideInformation;
import no.uib.hplims.models.peptideinformation.ResponseCurve;
import no.uib.hplims.models.peptideinformation.SampleProcessingType;
import no.uib.hplims.tools.FileDownloadResource;

import com.vaadin.data.Container;
import com.vaadin.event.FieldEvents.FocusEvent;
import com.vaadin.event.FieldEvents.FocusListener;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.ProgressIndicator;
import com.vaadin.ui.Table;
import com.vaadin.ui.Table.ColumnGenerator;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Upload;
import com.vaadin.ui.Upload.FailedEvent;
import com.vaadin.ui.Upload.FailedListener;
import com.vaadin.ui.Upload.FinishedEvent;
import com.vaadin.ui.Upload.FinishedListener;
import com.vaadin.ui.Upload.ProgressListener;
import com.vaadin.ui.Upload.Receiver;
import com.vaadin.ui.Upload.StartedEvent;
import com.vaadin.ui.Upload.StartedListener;
import com.vaadin.ui.Upload.SucceededEvent;
import com.vaadin.ui.Upload.SucceededListener;
import com.vaadin.ui.themes.Runo;

public class ResponseCurveView extends AbstractInformationView<ResponseCurve> {

	private static final long serialVersionUID = 5214237520319078831L;

	private File file = null;
	private Label status = new Label("Please select a file to upload");
	private Upload upload = null;
	private HorizontalLayout progressLayout = new HorizontalLayout();
	private ProgressIndicator pi = new ProgressIndicator();

	protected ResponseCurveView(List<ResponseCurve> list, PeptideInformation pi) {
		super(ResponseCurve.class, pi);

		setCaption("Response Curve");

		init(list);

		addVisibleColumns();
	}

	@Override
	protected void addVisibleColumns() {
		visibleColumnIds.add("processingType");
		visibleColumnIds.add("responseCurveFile");
		visibleColumnIds.add("areaStart");
		visibleColumnIds.add("areaEnd");
		visibleColumnIds.add("value");

		visibleColumnLabels.add("Type");
		visibleColumnLabels.add("File");
		visibleColumnLabels.add("From");
		visibleColumnLabels.add("To");
		visibleColumnLabels.add("Value");

		itemTable.setVisibleColumns(visibleColumnIds.toArray());
		itemTable.setColumnHeaders(visibleColumnLabels.toArray(new String[0]));

		itemTable.addGeneratedColumn("responseCurveFile",
				responseCurveFileColumn);

	}

	public void buttonClick(ClickEvent event) {
		if (event.getButton().equals(editButton)) {

			editMode = !editMode;
			editButtonClick(editMode);

		} else if (event.getButton().equals(newButton)) {

			ResponseCurve rc = new ResponseCurve();
			FacadeFactory.getFacade().store(rc);
			itemContainer.addBean(rc);
			editMode = true;
			editButtonClick(editMode);

		}

	}

	private void editButtonClick(boolean edit) {
		itemTable.setEditable(edit);
		itemTable.setTableFieldFactory(getFieldFactory());

		if (edit) {
			editButton.setCaption("Save");
			itemTable.removeGeneratedColumn("responseCurveFile");
		} else {
			editButton.setCaption("Edit");
			itemTable.addGeneratedColumn("responseCurveFile",
					responseCurveFileColumn);

			for (ResponseCurve rc : itemContainer.getItemIds()) {
				peptideInformation.addResponseCurve(rc);
				FacadeFactory.getFacade().store(rc);
			}
			FacadeFactory.getFacade().store(peptideInformation);
		}
	}

	@Override
	protected DefaultFieldFactory getFieldFactory() {
		if (itemTableFieldFactory == null) {
			itemTableFieldFactory = new DefaultFieldFactory() {

				private static final long serialVersionUID = -6483548250027205519L;

				@Override
				public Field createField(Container container,
						final Object itemId, Object propertyId,
						Component uiContext) {

					// Create fields by their class
					Class<?> cls = container.getType(propertyId);

					if (cls.equals(SampleProcessingType.class)) {
						NativeSelect ns = new NativeSelect();
						ns.setNullSelectionAllowed(false);
						ns.setImmediate(true);
						ns.setSizeFull();

						for (SampleProcessingType spt : SampleProcessingType
								.values()) {
							ns.addItem(spt);
						}

						ResponseCurve rc = (ResponseCurve) itemId;
						ns.setValue(rc.getProcessingType());

						return ns;
					}

					if (cls.equals(byte[].class)) {

						final TextField tf = new TextField();
						tf.setDescription("Select to upload a new file");

						tf.addListener(new FocusListener() {
							private static final long serialVersionUID = -2916515108437115693L;

							public void focus(FocusEvent event) {

								status.setValue("Please select a file to upload");

								addComponent(status);

								final Upload uploader = new Upload();
								uploader.setImmediate(true);
								uploader.setReceiver(receiver);

								uploader.addListener(new FailedListener() {
									private static final long serialVersionUID = -1368758591308275703L;

									public void uploadFailed(FailedEvent event) {
										// This method gets called when the
										// upload failed
										status.setValue("Uploading interrupted");
									}
								});

								uploader.addListener(new FinishedListener() {
									private static final long serialVersionUID = 8994720710595402890L;

									public void uploadFinished(
											FinishedEvent event) {
										// This method gets called always
										// when the upload finished,
										// either succeeding or failing
										progressLayout.setVisible(false);
										uploader.setVisible(false);
										// upload.setCaption("Select another file");

									}
								});

								uploader.addListener(new ProgressListener() {
									private static final long serialVersionUID = -2677371011698936106L;

									public void updateProgress(long readBytes,
											long contentLength) {
										// This method gets called several
										// times during the update
										pi.setValue(new Float(readBytes
												/ (float) contentLength));
									}
								});

								uploader.addListener(new StartedListener() {
									private static final long serialVersionUID = -2069492826537111025L;

									public void uploadStarted(StartedEvent event) {
										// This method gets called
										// immediatedly after upload is
										// started
										uploader.setVisible(false);
										progressLayout.setVisible(true);
										pi.setValue(0f);
										pi.setPollingInterval(500);
										status.setValue("Uploading file \""
												+ event.getFilename() + "\"");

									}
								});

								uploader.addListener(new SucceededListener() {
									private static final long serialVersionUID = 4863562618359796660L;

									public void uploadSucceeded(
											SucceededEvent event) {
										// This method gets called when the
										// upload finished successfully
										status.setValue("Uploading file \""
												+ event.getFilename()
												+ "\" succeeded");

										// ResponseCurve rc =
										// (ResponseCurve) itemId;
										// rc.setResponseCurveFile(file);
										// FacadeFactory.getFacade().store(rc);
										byte[] array = null;
										try {
											InputStream is = new FileInputStream(
													file);
											array = IOUtils.toByteArray(is);
											is.close();
										} catch (FileNotFoundException e) {
											// TODO Auto-generated catch
											// block
											e.printStackTrace();
										} catch (IOException e) {
											// TODO Auto-generated catch
											// block
											e.printStackTrace();
										}

										if (file != null) {
											ResponseCurve rc = (ResponseCurve) itemId;
											rc.setResponseCurveFileName(file
													.getName());
											tf.setValue(array);
										}
									}
								});

								if (upload == null) {
									addComponent(uploader);
								} else {
									replaceComponent(upload, uploader);
									upload = uploader;
								}

								progressLayout.setSpacing(true);
								progressLayout.setVisible(false);
								progressLayout.addComponent(pi);
								progressLayout.setComponentAlignment(pi,
										Alignment.MIDDLE_LEFT);
								final Button cancelProcessing = new Button(
										"Cancel");
								cancelProcessing
										.addListener(new Button.ClickListener() {
											private static final long serialVersionUID = -6090762289868973100L;

											public void buttonClick(
													ClickEvent event) {
												uploader.interruptUpload();
											}
										});
								cancelProcessing.setStyleName("small");
								progressLayout.addComponent(cancelProcessing);
								addComponent(progressLayout);

							}
						});

						return tf;
					}

					// Otherwise use the default field factory
					return super.createField(container, itemId, propertyId,
							uiContext);
				}
			};
		}

		return itemTableFieldFactory;
	}

	private Receiver receiver = new Receiver() {
		private static final long serialVersionUID = 3655126510196512344L;

		public OutputStream receiveUpload(String filename, String mimeType) {
			FileOutputStream fos = null;
			file = new File(filename);
			try {
				fos = new FileOutputStream(file);
			} catch (final java.io.FileNotFoundException e) {
				// Error while opening the file. Not reported here.
				e.printStackTrace();
				return null;
			}
			return fos;
		}
	};

	private ColumnGenerator responseCurveFileColumn = new ColumnGenerator() {
		private static final long serialVersionUID = 7287874364113683611L;

		public Object generateCell(Table source, Object itemId, Object columnId) {
			final ResponseCurve rc = (ResponseCurve) itemId;

			if (rc.getResponseCurveFile() != null) {
				/*
				 * Implemented as a button. Implementation could also be based
				 * on the Link class, but this would increase memory consumption
				 * dramatically.
				 */
				Button b = new Button(rc.getResponseCurveFileName());
				b.setImmediate(true);
				b.setStyleName(Runo.BUTTON_LINK);
				b.addListener(new ClickListener() {
					private static final long serialVersionUID = -6416025655992239108L;

					public void buttonClick(ClickEvent event) {
						try {
							FileOutputStream fos = new FileOutputStream(rc
									.getResponseCurveFileName());
							fos.write(rc.getResponseCurveFile());
							fos.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

						File f = new File(rc.getResponseCurveFileName());

						MyVaadinApplication app = MyVaadinApplication
								.getInstance();
						FileDownloadResource fdr = new FileDownloadResource(f,
								app);
						app.getMainWindow().open(fdr);
					}
				});

				return b;
			} else {
				return new Label("No file connected yet.");
			}
		}

	};

}
